Open Method#
강좌: 수치해석
Newton-Raphson Method#
알고리즘#
함수 \(f(x)\) 가 미분 가능하고 연속함수인 경우에 대해서 Taylor Expansion 을 이용하면 다음과 같다.
여기서 \(\xi \in (x_i, x_{i+1})\) 이다. 선형 근사하고, \(x_{i+1}\) 에서 x 축과 교점이 생기는 경우 다음과 같다.
즉, 아래 식을 반복적으로 해석해서 계산할 수 있다.

Fig. 9 Newthon Method (from Wikipedia)#
종료 판정 기준#
Bracketing method와 동일하게 근사 상대 오차 \(\epsilon_a\) 의 크기가 \(tol\) 보다 작으면 멈춘다.
Note
\(\epsilon_a< tol\) 이면 멈춘다.
예제 1#
\(f(x) = e^{-x} - x\) 의 근을 구하시오. 초기 가정은 \(x_0 = 0\) 이다.
By Hand#
도함수 \(f'(x) = -e^{-x} - 1\) 이다.
Newton-Raphson 식은 아래와 같다.
%matplotlib inline
from matplotlib import pyplot as plt
import numpy as np
plt.style.use('ggplot')
plt.rcParams['figure.dpi'] = 150
x = np.linspace(-5, 5, 201)
plt.plot(x, np.exp(-x) - x)
plt.xlabel('x')
plt.ylabel('y')
plt.legend(["$e^{-x} - x$"])
<matplotlib.legend.Legend at 0x7fe95bf43590>

# First Step
x0 = 0
x1 = x0 - (np.exp(-x0) - x0) / (-np.exp(-x0) - 1)
print("x1 = {:.4f}, err_a = {:.3e}".format(x1, abs((x1 - x0)/x1)))
x1 = 0.5000, err_a = 1.000e+00
# Second step
x0 = x1
x1 = x0 - (np.exp(-x0) - x0) / (-np.exp(-x0) - 1)
print("x1 = {:.4f}, err_a = {:.3e}".format(x1, abs((x1 - x0)/x1)))
x1 = 0.5663, err_a = 1.171e-01
# Thrid step
x0 = x1
x1 = x0 - (np.exp(-x0) - x0) / (-np.exp(-x0) - 1)
print("x1 = {:.4f}, err_a = {:.3e}".format(x1, abs((x1 - x0)/x1)))
x1 = 0.5671, err_a = 1.467e-03
# Fourth step
x0 = x1
x1 = x0 - (np.exp(-x0) - x0) / (-np.exp(-x0) - 1)
print("x1 = {:.4f}, err_a = {:.3e}".format(x1, abs((x1 - x0)/x1)))
x1 = 0.5671, err_a = 2.211e-07
# Bisection Method
from scipy.optimize import root_scalar
f = lambda x: np.exp(-x) - x
root_scalar(f, bracket=[0, 1], method='bisect')
converged: True
flag: 'converged'
function_calls: 41
iterations: 39
root: 0.5671432904109679
Quadratic Convergence#
엄밀해를 \(x_r\) 로 가정하자 (\(f(x_r) = 0)\). \(x_i\)를 중심으로 한 Taylor Expansion식을 적용하면 다음과 같다.
이 식에서 Newton-Raphson 식을 빼면 다음과 같다.
엄밀해로 부터 오차를 다음과 같이 정의하면
위 식을 아래와 같이 정리할 수 있다.
해가 수렴하는 경우 \(x_i, \eta \rightarrow x_r\) 이므로,
즉 현재의 오차는 이전 단계에서의 오차에 제곱에 비례한다.
예제#
위 예제에서 참값은 \(x_r=0.5671432904109679\) 이다. 오차 \(E_i\) 의 변화를 계산해보자.
# Function and derivatives
f = lambda x : np.exp(-x) - x
fp = lambda x: -np.exp(-x) - 1
x0 = x = 0
xr = 0.5671432904109679
Err = []
itmax = 5
for i in range(itmax):
# Newton-Raphson
xn = x - f(x) / fp(x)
# Compute
Ei = xr - x
Err.append(Ei)
x = xn
print(Ei)
0.5671432904109679
0.06714329041096789
0.0008322872137497273
1.253761057196101e-07
1.1868284133242923e-12
-np.exp(-xr) / (2*fp(xr))
0.1809481283173079
위 오차식을 예제에 적용하면
초기 오차 \(E_0 = x_r - 0\) 이므로, 이 식으로 계산한 오차는 다음과 같다.
매 계산마다 정확한 유효자릿수가 2개가 된다.
# 실제 오차, 추정식
for i in range(1, 5):
print(Err[i], 0.18095*Err[i-1]**2)
0.06714329041096789 0.058202841070737574
0.0008322872137497273 0.0008157626708729342
1.253761057196101e-07 1.2534442801669386e-07
1.1868284133242923e-12 2.8443834288658173e-15
예제 2#
아래 함수의 근을 구하시오. 초기값은 0.5로 하자.
Newton-Raphson 법을 적용하면 다음과 같다.
# Function and derivatives
f = lambda x : x**10 - 1
fp = lambda x: 10*x**9
x0 = x = 0.5
Err = []
itmax = 45
for i in range(itmax):
# Newton-Raphson
xn = x - f(x) / fp(x)
# Compute difference
Eps = xn - x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = 51.6500, err = 5.115e+01
x1 = 46.4850, err = -5.165e+00
x1 = 41.8365, err = -4.648e+00
x1 = 37.6529, err = -4.184e+00
x1 = 33.8876, err = -3.765e+00
x1 = 30.4988, err = -3.389e+00
x1 = 27.4489, err = -3.050e+00
x1 = 24.7040, err = -2.745e+00
x1 = 22.2336, err = -2.470e+00
x1 = 20.0103, err = -2.223e+00
x1 = 18.0092, err = -2.001e+00
x1 = 16.2083, err = -1.801e+00
x1 = 14.5875, err = -1.621e+00
x1 = 13.1287, err = -1.459e+00
x1 = 11.8159, err = -1.313e+00
x1 = 10.6343, err = -1.182e+00
x1 = 9.5708, err = -1.063e+00
x1 = 8.6138, err = -9.571e-01
x1 = 7.7524, err = -8.614e-01
x1 = 6.9771, err = -7.752e-01
x1 = 6.2794, err = -6.977e-01
x1 = 5.6515, err = -6.279e-01
x1 = 5.0863, err = -5.651e-01
x1 = 4.5777, err = -5.086e-01
x1 = 4.1199, err = -4.578e-01
x1 = 3.7079, err = -4.120e-01
x1 = 3.3371, err = -3.708e-01
x1 = 3.0034, err = -3.337e-01
x1 = 2.7031, err = -3.003e-01
x1 = 2.4328, err = -2.703e-01
x1 = 2.1896, err = -2.432e-01
x1 = 1.9707, err = -2.189e-01
x1 = 1.7738, err = -1.968e-01
x1 = 1.5970, err = -1.768e-01
x1 = 1.4388, err = -1.582e-01
x1 = 1.2987, err = -1.401e-01
x1 = 1.1784, err = -1.204e-01
x1 = 1.0833, err = -9.500e-02
x1 = 1.0237, err = -5.969e-02
x1 = 1.0023, err = -2.135e-02
x1 = 1.0000, err = -2.292e-03
x1 = 1.0000, err = -2.393e-05
x1 = 1.0000, err = -2.578e-09
x1 = 1.0000, err = 0.000e+00
x1 = 1.0000, err = 0.000e+00
예제 3#
아래 함수의 해를 적절한 초기값으로 부터 근을 구하시오.
Newton-Raphson 법을 적용하면 다음과 같다.
# Function and derivatives
f = lambda x : x**2 + 10*np.sin(x)
fp = lambda x: 2*x + 10*np.cos(x)
x = np.arange(-10, 10, 0.1)
plt.plot(x, f(x))
plt.xlabel('x')
plt.ylabel('y')
plt.legend(["$x^2 + 10\sin(x)$"])
<matplotlib.legend.Legend at 0x7fe95bfb1210>

# 초기값 -4
x0 = x = -4
Err = []
itmax = 5
for i in range(itmax):
# Newton-Raphson
xn = x - f(x) / fp(x)
# Compute difference
Eps = xn - x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = -2.3787, err = 1.621e+00
x1 = -2.4832, err = -1.045e-01
x1 = -2.4795, err = 3.666e-03
x1 = -2.4795, err = 4.253e-06
x1 = -2.4795, err = 5.736e-12
# 초기값 1
x0 = x = 1
Err = []
itmax = 5
for i in range(itmax):
# Newton-Raphson
xn = x - f(x) / fp(x)
# Compute difference
Eps = xn - x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = -0.2717, err = -1.272e+00
x1 = 0.0154, err = 2.872e-01
x1 = 0.0000, err = -1.541e-02
x1 = 0.0000, err = -2.251e-05
x1 = 0.0000, err = -5.067e-11
# 초기값 2.5
x0 = x = 2.5
Err = []
itmax = 50
for i in range(itmax):
# Newton-Raphson
xn = x - f(x) / fp(x)
# Compute difference
Eps = xn - x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = 6.5628, err = 4.063e+00
x1 = 4.5472, err = -2.016e+00
x1 = 3.0957, err = -1.451e+00
x1 = 5.7397, err = 2.644e+00
x1 = 4.3537, err = -1.386e+00
x1 = 2.5082, err = -1.846e+00
x1 = 6.5195, err = 4.011e+00
x1 = 4.5492, err = -1.970e+00
x1 = 3.1005, err = -1.449e+00
x1 = 5.7449, err = 2.644e+00
x1 = 4.3563, err = -1.389e+00
x1 = 2.5186, err = -1.838e+00
x1 = 6.4670, err = 3.948e+00
x1 = 4.5496, err = -1.917e+00
x1 = 3.1014, err = -1.448e+00
x1 = 5.7459, err = 2.645e+00
x1 = 4.3568, err = -1.389e+00
x1 = 2.5206, err = -1.836e+00
x1 = 6.4575, err = 3.937e+00
x1 = 4.5495, err = -1.908e+00
x1 = 3.1010, err = -1.448e+00
x1 = 5.7455, err = 2.644e+00
x1 = 4.3566, err = -1.389e+00
x1 = 2.5197, err = -1.837e+00
x1 = 6.4618, err = 3.942e+00
x1 = 4.5495, err = -1.912e+00
x1 = 3.1012, err = -1.448e+00
x1 = 5.7457, err = 2.645e+00
x1 = 4.3567, err = -1.389e+00
x1 = 2.5201, err = -1.837e+00
x1 = 6.4596, err = 3.939e+00
x1 = 4.5495, err = -1.910e+00
x1 = 3.1011, err = -1.448e+00
x1 = 5.7456, err = 2.645e+00
x1 = 4.3566, err = -1.389e+00
x1 = 2.5199, err = -1.837e+00
x1 = 6.4606, err = 3.941e+00
x1 = 4.5495, err = -1.911e+00
x1 = 3.1011, err = -1.448e+00
x1 = 5.7456, err = 2.645e+00
x1 = 4.3567, err = -1.389e+00
x1 = 2.5200, err = -1.837e+00
x1 = 6.4601, err = 3.940e+00
x1 = 4.5495, err = -1.911e+00
x1 = 3.1011, err = -1.448e+00
x1 = 5.7456, err = 2.645e+00
x1 = 4.3566, err = -1.389e+00
x1 = 2.5200, err = -1.837e+00
x1 = 6.4604, err = 3.940e+00
x1 = 4.5495, err = -1.911e+00
문제점#
수렴하지 않을 수 있다.
Local extrema 근처에서 진동할 수 있다.
초기 추정 값이 중요함
DIY#
Newton-Raphson method 함수를 구성하시오. 아래 판별 기준을 적용하라.
\(f(x_i)\) 값이 tolerance 보다 작으면 멈춘다.
근사 상대 오차 \(\epsilon_a\) 가 tolerance 보다 작으면 멈춘다.
\(f'(x_i) = 0\) 인 경우 에러를 발생시킨다 (
raise ValueError
)
def newton(f, fp, x0, rtol=1e-6):
# DIY
...
Secant Method#
Newton-Raphson 방법은 미분 함수 \(f'(x)\) 가 필요하다. 미분을 구하기 힘들 경우 수치적으로 근사한다.
Fig. 10 Secant Method (from Wikipedia)#
그림과 같이 \({i-1}\), \({i}\) 번째 결과로 부터 기울기를 대신 사용한다.
이 근사 미분값을 사용하는 방법이 Secant method 이다.
예제 1#
다음 함수 \(f(x) = e^{-x} - x\) 의 근을 구하시오. Secant Method로 해석해보자. (초기 값은 \(x_{-1} = 0, x_0 = 1\) 로 한다.)
# Function and derivatives
f = lambda x : np.exp(-x) - x
xm, x0 = 0.0, 1.0
x = x0
Err = []
itmax = 5
for i in range(itmax):
# Scecant Method
xn = x - f(x)*(x - xm) / (f(x) - f(xm))
# Compute difference
Eps = xn - x
xm = x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = 0.6127, err = -3.873e-01
x1 = 0.5638, err = -4.886e-02
x1 = 0.5672, err = 3.332e-03
x1 = 0.5671, err = -2.705e-05
x1 = 0.5671, err = -1.620e-08
예제 2#
다음 함수 \(f(x) = log(x)\) 의 근을 구하시오. Secant Method로 해석해보자. (초기 값은 \(x_{-1} = 0.5, x_0 = 5\) 로 한다.)
# Function and derivatives
f = lambda x : np.log(x)
xm, x0 = 0.5, 5
x = x0
Err = []
itmax = 5
for i in range(itmax):
# Scecant Method
xn = x - f(x)*(x - xm) / (f(x) - f(xm))
# Compute difference
Eps = xn - x
xm = x
x = xn
print("x1 = {:.4f}, err = {:.3e}".format(x, Eps))
x1 = 1.8546, err = -3.145e+00
x1 = -0.1044, err = -1.959e+00
x1 = nan, err = nan
x1 = nan, err = nan
x1 = nan, err = nan
/tmp/ipykernel_11119/3825093768.py:2: RuntimeWarning: invalid value encountered in log
f = lambda x : np.log(x)
root_scalar(f, bracket=[0.5, 5], method='bisect')
converged: True
flag: 'converged'
function_calls: 44
iterations: 42
root: 0.9999999999998863
특징#
미분 함수 없이 계산 가능하다.
수렴 속도가 Newton-Raphson과 거의 비슷하나 약간 늦다.
발산할 수 있다.
DIY#
Scecant method 함수를 구성하시오. Newton-Raphson 과 동일한 판별 조건을 사용하자.
def secant(f, x0, x1, rtol=1e-6):
# DIY
...
SciPy 활용#
# root_scalar?
# Function and derivatives
f = lambda x : np.exp(-x) - x
fp = lambda x: -np.exp(-x) - 1
root_scalar(f, fprime=fp, x0=1, method='newton')
converged: True
flag: 'converged'
function_calls: 8
iterations: 4
root: 0.5671432904097838
root_scalar(f, x0=0, x1=1, method='secant')
converged: True
flag: 'converged'
function_calls: 7
iterations: 6
root: 0.567143290409784